#include "../../Include/Utils/WizUtilsUTF.hpp"

namespace Wiz
{
    namespace UTF8
    {
        ////////////////////////////////////////////////////////////////////////
        ::Wiz::Size::Type FromUCS2(::Wiz::UInt8::OutPtr Result, ::Wiz::UInt16::In UCS2)
        {
            if (UCS2 < 0x0080)
            {
                /// UCS2: 0000 ~ 007F => UTF-8: 0xxxxxxx
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(UCS2);
                return 1;
            }
            else if (UCS2 < 0x0800)
            {
                /// UCS2: 0080 ~ 07FF => UTF-8: 110xxxxx 10xxxxxx
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 5>(UCS2) | 0x6 << 5);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS2) | 0x2 << 6);
                return 2;
            }
            else
            {
                /// UCS2: 0800 ~ FFFF => UTF-8: 1110xxxx 10xxxxxx(rep : 2)
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<12, 4>(UCS2) | 0xE << 4);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 6>(UCS2) | 0x2 << 6);
                Result[2] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS2) | 0x2 << 6);
                return 3;
            }
        } /// end of FromUCS2

        ::Wiz::Size::Type ToUCS2(::Wiz::UInt16::Out Result, ::Wiz::UInt8::InPtr UTF8Ptr)
        {
            if (UTF8Ptr[0] < 0xC0)
            {
                /// UTF-8: 0xxxxxxx => UCS2: 0000 ~ 007F
                Result = ::Wiz::Cast::Static<::Wiz::UInt16::Type>(UTF8Ptr[0]);
                return 1;
            }
            else if (UTF8Ptr[0] < 0xE0)
            {
                /// UTF-8: 110xxxxx 10xxxxxx => UCS2: 0080 ~ 07FF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<5>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                return 2;
            }
            else
            {
                /// UTF-8: 1110xxxx 10xxxxxx(rep : 2) => UCS2: 0800 ~ FFFF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<4>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[2]));
                return 3;
            }
        } /// end of ToUCS2
        ////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////
        ::Wiz::Size::Type FromUCS4(::Wiz::UInt8::OutPtr Result, ::Wiz::UInt32::In UCS4)
        {
            if (UCS4 < 0x0080)
            {
                /// UCS2: 0000 ~ 007F => UTF-8: 0xxxxxxx
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(UCS4);
                return 1;
            }
            else if (UCS4 < 0x0800)
            {
                /// UCS2: 0080 ~ 07FF => UTF-8: 110xxxxx 10xxxxxx
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 5>(UCS4) | 0x6 << 5);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS4) | 0x2 << 6);
                return 2;
            }
            else if (UCS4 < 0x10000)
            {
                /// UCS2: 0800 ~ FFFF => UTF-8: 1110xxxx 10xxxxxx(rep : 2)
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<12, 4>(UCS4) | 0xE << 4);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 6>(UCS4) | 0x2 << 6);
                Result[2] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS4) | 0x2 << 6);
                return 3;
            }
            else if (UCS4 < 0x20000)
            {
                /// UCS4: 00010000 ~ 001FFFFF => UTF-8: 11110xxx 10xxxxxx(rep : 3)
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<18, 3>(UCS4) | 0x1E << 3);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<12, 6>(UCS4) | 0x2 << 6);
                Result[2] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 6>(UCS4) | 0x2 << 6);
                Result[3] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS4) | 0x2 << 6);
                return 4;
            }
            else if (UCS4 < 0x04000000)
            {
                /// UCS4: 00200000 ~ 03FFFFFF => UTF-8: 111110xx 10xxxxxx(rep : 4)
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<24, 2>(UCS4) | 0x3E << 2);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<18, 6>(UCS4) | 0x2 << 6);
                Result[2] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<12, 6>(UCS4) | 0x2 << 6);
                Result[3] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 6>(UCS4) | 0x2 << 6);
                Result[4] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS4) | 0x2 << 6);
                return 5;
            }
            else
            {
                /// UCS4: 04000000 ~ 7FFFFFFF => UTF-8: 1111110x 10xxxxxx(rep : 5)
                Result[0] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<30, 1>(UCS4) | 0x7E << 1);
                Result[1] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<24, 6>(UCS4) | 0x2 << 6);
                Result[2] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<18, 6>(UCS4) | 0x2 << 6);
                Result[3] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<12, 6>(UCS4) | 0x2 << 6);
                Result[4] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<6, 6>(UCS4) | 0x2 << 6);
                Result[5] = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetBitsToLow<0, 6>(UCS4) | 0x2 << 6);
                return 6;
            }
        } /// end of FromUCS4

        ::Wiz::Size::Type ToUCS4(::Wiz::UInt32::Out Result, ::Wiz::UInt8::InPtr UTF8Ptr)
        {
            if (UTF8Ptr[0] < 0xC0)
            {
                /// UTF-8: 0xxxxxxx => UCS2: 0000 ~ 007F
                Result = ::Wiz::Cast::Static<::Wiz::UInt16::Type>(UTF8Ptr[0]);
                return 1;
            }
            else if (UTF8Ptr[0] < 0xE0)
            {
                /// UTF-8: 110xxxxx 10xxxxxx => UCS2: 0080 ~ 07FF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<5>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                return 2;
            }
            else if (UTF8Ptr[0] < 0xF0)
            {
                /// UTF-8: 1110xxxx 10xxxxxx(rep : 2) => UCS2: 0800 ~ FFFF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<4>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[2]));
                return 3;
            }
            else if (UTF8Ptr[0] < 0xF8)
            {
                /// UTF-8: 11110xxx 10xxxxxx(rep : 3) = > UCS4: 00010000 ~ 001FFFFF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<3>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[2]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[3]));
                return 4;
            }
            else if (UTF8Ptr[0] < 0xFC)
            {
                /// UTF-8: 111110xx 10xxxxxx(rep : 4) = > UCS4: 00200000 ~ 03FFFFFF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<2>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[2]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[3]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[4]));
                return 5;
            }
            else
            {
                /// UTF-8: 1111110x 10xxxxxx(rep : 5) = > UCS4: 04000000 ~ 7FFFFFFF
                Result  = ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<1>(UTF8Ptr[0]) << 6);
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[1]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[2]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[3]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[4]));
                Result <<= 6;
                Result |= ::Wiz::Cast::Static<::Wiz::UInt8::Type>(::Wiz::Bit::GetLowBits<6>(UTF8Ptr[5]));
                return 6;
            }
        } /// end of ToUCS2
    } /// end of namespace UTF8
    ////////////////////////////////////////////////////////////////////////////
} /// end of namespace Wiz
